package com.pokermind.dse.tcp.client.preset;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.pokermind.dse.tcp.client.SimpleTcpClient;
import com.pokermind.dse.tcp.protocol.ProtocolConstants;

import java.util.*;
import java.util.concurrent.CompletableFuture;

/**
 * 预设场景测试客户端 - 自动化执行预设的牌局流程
 * 
 * @author PokerMind Team
 */
public class PresetScenarioClient {
    
    private SimpleTcpClient tcpClient;
    private PresetScenario scenario;
    private String handId;
    private Map<String, Integer> playerStacks;
    private Map<String, Integer> playerCurrentBets;
    private int currentPot;
    private int lastGlobalSequence = 0;  // 记录上次查询的最大序列号，用于增量查询
    private ObjectMapper objectMapper = new ObjectMapper();
    private Set<String> foldedPlayers = new HashSet<>();  // 记录已弃牌的玩家
    private java.sql.Connection dbConnection;  // 数据库连接（可选）
    
    // AI 决策集成相关
    private GameStateTracker gameState;  // 游戏状态追踪器
    private boolean enableAI = true;  // 是否启用AI决策（默认启用）
    private String currentStreet = "preflop";  // 当前街道
    
    public PresetScenarioClient(String host, int port, PresetScenario scenario) throws Exception {
        this.tcpClient = new SimpleTcpClient(host, port);
        this.scenario = scenario;
        this.playerStacks = new HashMap<>();
        this.playerCurrentBets = new HashMap<>();
        this.currentPot = 0;
        
        // 初始化GameStateTracker
        this.gameState = new GameStateTracker();
        
        // 初始化玩家筹码
        for (PresetScenario.PlayerConfig player : scenario.getPlayers().values()) {
            playerStacks.put(player.getPlayerId(), player.getInitialStack());
            playerCurrentBets.put(player.getPlayerId(), 0);
            
            // 同时初始化GameStateTracker
            gameState.initPlayer(player.getPlayerId(), player.getInitialStack());
        }
    }
    
    /**
     * 连接数据库（用于查询验证）
     */
    public void connectDatabase(String jdbcUrl, String username, String password) {
        try {
            this.dbConnection = java.sql.DriverManager.getConnection(jdbcUrl, username, password);
            System.out.println("✅ 已连接数据库");
        } catch (Exception e) {
            System.err.println("⚠️ 数据库连接失败: " + e.getMessage());
        }
    }
    
    /**
     * 只查询手牌状态（用于街道转换后验证）
     */
    private void queryHandStateOnly(String step) {
        if (dbConnection == null) {
            return;
        }
        
        System.out.println();
        System.out.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
        System.out.println("  📊 手牌状态验证 [" + step + "]");
        System.out.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
        
        try {
            String handSql = "SELECT current_street, board_cards, current_pot, status FROM poker_hands WHERE hand_id = ?";
            try (java.sql.PreparedStatement ps = dbConnection.prepareStatement(handSql)) {
                ps.setString(1, handId);
                try (java.sql.ResultSet rs = ps.executeQuery()) {
                    if (rs.next()) {
                        String currentStreet = rs.getString("current_street");
                        String boardCards = rs.getString("board_cards");
                        java.math.BigDecimal currentPotDB = rs.getBigDecimal("current_pot");
                        String status = rs.getString("status");
                        
                        System.out.printf("  当前街道: %s | 状态: %s%n", currentStreet, status);
                        System.out.printf("  公共牌: %s%n", boardCards != null && !boardCards.isEmpty() ? boardCards : "无");
                        System.out.printf("  当前底池: %s", currentPotDB);
                        if (currentPotDB != null && currentPotDB.intValue() == currentPot) {
                            System.out.print(" ✓");
                        } else {
                            System.out.printf(" ⚠️  [客户端: %d]", currentPot);
                        }
                        System.out.println();
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("❌ 查询失败: " + e.getMessage());
        }
        System.out.println();
    }
    
    /**
     * 查询并打印数据库记录（街道结束后验证用）
     */
    private void queryAndPrintDB(String step) {
        if (dbConnection == null) {
            return;
        }
        
        System.out.println();
        System.out.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
        System.out.println("  📊 数据库记录 [" + step + "]");
        System.out.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
        
        try {
            // 1. 先通过业务 hand_id 查询手牌记录（查询所有关键字段）
            String handSql = "SELECT id, hand_id, table_id, user_id, button_seat, status, current_street, " +
                           "players_dealt, board_cards, current_pot, small_blind, big_blind, ante, " +
                           "final_pot, rake_amount, create_time, run_it_twice, run_twice_details FROM poker_hands WHERE hand_id = ?";
            Long handPrimaryId = null;
            String tableId = null;
            
            // 先显示原始SQL查询
            System.out.println("【SQL查询】");
            System.out.println("  查询语句: SELECT * FROM poker_hands WHERE hand_id = '" + handId + "'");
            System.out.println();
            
            try (java.sql.PreparedStatement ps = dbConnection.prepareStatement(handSql)) {
                ps.setString(1, handId);
                try (java.sql.ResultSet rs = ps.executeQuery()) {
                    if (rs.next()) {
                        handPrimaryId = rs.getLong("id");
                        tableId = rs.getString("table_id");
                        
                        // 显示原始数据库记录
                        System.out.println("【原始数据 - poker_hands】");
                        System.out.println("  id: " + rs.getLong("id"));
                        System.out.println("  hand_id: " + rs.getString("hand_id"));
                        System.out.println("  table_id: " + rs.getString("table_id"));
                        System.out.println("  user_id: " + rs.getLong("user_id"));
                        System.out.println("  button_seat: " + rs.getInt("button_seat"));
                        System.out.println("  status: " + rs.getString("status"));
                        System.out.println("  current_street: " + rs.getString("current_street"));
                        System.out.println("  players_dealt: " + rs.getInt("players_dealt"));
                        System.out.println("  board_cards: " + rs.getString("board_cards"));
                        System.out.println("  current_pot: " + rs.getBigDecimal("current_pot"));
                        System.out.println("  small_blind: " + rs.getBigDecimal("small_blind"));
                        System.out.println("  big_blind: " + rs.getBigDecimal("big_blind"));
                        System.out.println("  ante: " + rs.getBigDecimal("ante"));
                        System.out.println("  final_pot: " + rs.getBigDecimal("final_pot"));
                        System.out.println("  rake_amount: " + rs.getBigDecimal("rake_amount"));
                        System.out.println("  create_time: " + rs.getLong("create_time"));
                        
                        // V4.7: 显示 Run It Twice 字段
                        try {
                            boolean runItTwice = rs.getBoolean("run_it_twice");
                            if (!rs.wasNull()) {
                                System.out.println("  run_it_twice: " + (runItTwice ? "✅ TRUE" : "FALSE"));
                            } else {
                                System.out.println("  run_it_twice: NULL");
                            }
                        } catch (java.sql.SQLException e) {
                            // 字段不存在时的SQL异常
                            System.out.println("  run_it_twice: ❌ 字段不存在 (" + e.getMessage() + ")");
                        } catch (Exception e) {
                            System.out.println("  run_it_twice: ⚠️  读取错误: " + e.getMessage());
                        }
                        
                        try {
                            String runTwiceDetails = rs.getString("run_twice_details");
                            if (runTwiceDetails != null && !runTwiceDetails.isEmpty()) {
                                System.out.println("  run_twice_details: ✅ " + runTwiceDetails.length() + " 字符");
                                // 格式化显示JSON（简化版）
                                if (runTwiceDetails.length() < 500) {
                                    System.out.println("    " + runTwiceDetails);
                                } else {
                                    System.out.println("    " + runTwiceDetails.substring(0, 200) + "... (共" + runTwiceDetails.length() + "字符)");
                                }
                            } else {
                                System.out.println("  run_twice_details: NULL");
                            }
                        } catch (java.sql.SQLException e) {
                            // 字段不存在时的SQL异常
                            System.out.println("  run_twice_details: ❌ 字段不存在 (" + e.getMessage() + ")");
                        } catch (Exception e) {
                            System.out.println("  run_twice_details: ⚠️  读取错误: " + e.getMessage());
                        }
                        
                        System.out.println();
                        
                        // 显示格式化的验证信息
                        System.out.println("【格式化显示 + 验证】");
                        System.out.printf("  ID: %d | 业务ID: %s | 桌子: %s%n", 
                            handPrimaryId, rs.getString("hand_id"), rs.getString("table_id"));
                        System.out.printf("  状态: %s | 街道: %s | 按钮座位: %d%n", 
                            rs.getString("status"),
                            rs.getString("current_street"),
                            rs.getInt("button_seat"));
                        System.out.printf("  发牌人数: %d | 盲注: %s/%s | 前注: %s%n",
                            rs.getInt("players_dealt"),
                            rs.getBigDecimal("small_blind"),
                            rs.getBigDecimal("big_blind"),
                            rs.getBigDecimal("ante"));
                        
                        // 公共牌
                        String boardCards = rs.getString("board_cards");
                        if (boardCards != null && !boardCards.isEmpty()) {
                            System.out.printf("  公共牌: %s%n", boardCards);
                        }
                        
                        // V4.7: 验证 Run It Twice
                        Boolean ritEnabled = null;
                        String ritDetails = null;
                        try {
                            ritEnabled = rs.getBoolean("run_it_twice");
                            if (rs.wasNull()) ritEnabled = null;
                            ritDetails = rs.getString("run_twice_details");
                        } catch (Exception e) {
                            // 字段不存在，忽略
                        }
                        
                        if (ritEnabled != null && ritEnabled) {
                            System.out.println("  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
                            System.out.println("  🎲 Run It Twice 数据验证");
                            System.out.println("  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
                            System.out.println("  ✅ run_it_twice = TRUE");
                            if (ritDetails != null && !ritDetails.isEmpty()) {
                                System.out.println("  ✅ run_twice_details 已存储 (" + ritDetails.length() + " 字符)");
                                
                                // 简单解析验证关键信息
                                try {
                                    if (ritDetails.contains("\"enabled\":true") || ritDetails.contains("\"enabled\": true")) {
                                        System.out.println("  ✅ enabled = true");
                                    }
                                    int runsCount = 0;
                                    int idx = 0;
                                    while ((idx = ritDetails.indexOf("\"run\":", idx)) != -1) {
                                        runsCount++;
                                        idx++;
                                    }
                                    if (runsCount > 0) {
                                        System.out.println("  ✅ 运行次数 = " + runsCount);
                                    }
                                } catch (Exception e) {
                                    // 忽略解析错误
                                }
                            } else {
                                System.out.println("  ❌ run_twice_details 为空");
                            }
                            System.out.println("  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
                        }
                        
                        // 验证底池（使用 current_pot，因为 final_pot 只在手牌完成时有值）
                        java.math.BigDecimal dbCurrentPot = rs.getBigDecimal("current_pot");
                        java.math.BigDecimal dbFinalPot = rs.getBigDecimal("final_pot");
                        System.out.printf("  当前底池: %s", dbCurrentPot);
                        if (dbCurrentPot != null && dbCurrentPot.intValue() == currentPot) {
                            System.out.print(" ✓");
                        } else if (dbCurrentPot != null) {
                            System.out.printf(" ⚠️  [客户端: %d]", currentPot);
                        } else {
                            System.out.printf(" ⚠️  [客户端: %d, DB为null]", currentPot);
                        }
                        System.out.printf(" | 最终底池: %s%n", dbFinalPot != null ? dbFinalPot : "null");
                        System.out.println();
                    }
                }
            }
            
            // 2. 如果找到手牌记录，使用主键 id 查询行动记录（增量查询）
            if (handPrimaryId != null) {
                System.out.println("【SQL查询】");
                if (lastGlobalSequence == 0) {
                    System.out.println("  查询语句: SELECT * FROM poker_hand_actions WHERE hand_id = " + handPrimaryId + " ORDER BY global_sequence");
                } else {
                    System.out.println("  查询语句: SELECT * FROM poker_hand_actions WHERE hand_id = " + handPrimaryId + " AND global_sequence > " + lastGlobalSequence + " ORDER BY global_sequence");
                }
                System.out.println();
                
                // 增量查询：只查询新增的记录
                String actionSql = "SELECT id, hand_id, seat_number, player_id, user_id, street, action_sequence, " +
                                 "global_sequence, action_type, amount, stack, pot, " +
                                 "think_time_seconds, create_time " +
                                 "FROM poker_hand_actions WHERE hand_id = ? AND global_sequence > ? ORDER BY global_sequence";
                
                // 先读取所有数据到列表
                java.util.List<java.util.Map<String, Object>> actions = new java.util.ArrayList<>();
                int maxSequence = lastGlobalSequence;  // 记录本次查询的最大序列号
                try (java.sql.PreparedStatement ps = dbConnection.prepareStatement(actionSql)) {
                    ps.setLong(1, handPrimaryId);
                    ps.setInt(2, lastGlobalSequence);  // 增量查询参数
                    try (java.sql.ResultSet rs = ps.executeQuery()) {
                        while (rs.next()) {
                            java.util.Map<String, Object> action = new java.util.HashMap<>();
                            action.put("id", rs.getLong("id"));
                            action.put("table_id", tableId);  // 添加 table_id
                            action.put("hand_id", rs.getLong("hand_id"));
                            action.put("seat_number", rs.getInt("seat_number"));
                            action.put("player_id", rs.getString("player_id"));
                            action.put("user_id", rs.getLong("user_id"));
                            action.put("street", rs.getString("street"));
                            action.put("action_sequence", rs.getInt("action_sequence"));
                            int globalSeq = rs.getInt("global_sequence");
                            action.put("global_sequence", globalSeq);
                            action.put("action_type", rs.getString("action_type"));
                            action.put("amount", rs.getBigDecimal("amount"));
                            action.put("stack_after", rs.getBigDecimal("stack"));
                            action.put("pot_after", rs.getBigDecimal("pot"));
                            action.put("think_time_seconds", rs.getObject("think_time_seconds"));
                            action.put("create_time", rs.getLong("create_time"));
                            actions.add(action);
                            
                            // 更新最大序列号
                            if (globalSeq > maxSequence) {
                                maxSequence = globalSeq;
                            }
                        }
                    }
                }
                
                // 显示原始数据（紧凑格式）
                if (!actions.isEmpty()) {
                    int firstSeq = (Integer) actions.get(0).get("global_sequence");
                    int lastSeq = (Integer) actions.get(actions.size() - 1).get("global_sequence");
                    if (lastGlobalSequence == 0) {
                        System.out.printf("【原始数据 - poker_hand_actions】(共 %d 条)%n", actions.size());
                    } else {
                        System.out.printf("【新增记录 #%d - #%d】(共 %d 条)%n", firstSeq, lastSeq, actions.size());
                    }
                    
                    for (java.util.Map<String, Object> action : actions) {
                        // 修改显示格式：[street|street_seq|global_seq] [座位X] - 更清晰
                        System.out.printf("  #%-2d [%s|seq:%d|全局:%d] [座%d] %s 【%s】 金额:%s 筹码:%s 底池:%s%n",
                            action.get("global_sequence"),
                            action.get("street"),
                            action.get("action_sequence"),  // 街道序列
                            action.get("global_sequence"),  // 全局序列
                            action.get("seat_number"),      // 座位号
                            action.get("player_id"),
                            action.get("action_type"),
                            action.get("amount"),
                            action.get("stack_after"),
                            action.get("pot_after"));
                    }
                    System.out.println();
                    
                    // 显示格式化数据和验证
                    System.out.println("【格式化显示 + 验证】");
                    int count = 0;
                    boolean hasError = false;
                    
                    // 找出每个玩家的最后一条记录
                    java.util.Map<String, Integer> lastActionIndex = new java.util.HashMap<>();
                    for (int i = 0; i < actions.size(); i++) {
                        String playerId = (String) actions.get(i).get("player_id");
                        lastActionIndex.put(playerId, i);
                    }
                    
                    for (java.util.Map<String, Object> action : actions) {
                        count++;
                        String playerId = (String) action.get("player_id");
                        String actionType = (String) action.get("action_type");
                        java.math.BigDecimal amount = (java.math.BigDecimal) action.get("amount");
                        java.math.BigDecimal stackAfter = (java.math.BigDecimal) action.get("stack_after");
                        java.math.BigDecimal potAfter = (java.math.BigDecimal) action.get("pot_after");
                        
                        // 紧凑显示：一行显示所有关键信息
                        // 格式：#序号 [座位] 玩家 【行动类型】 金额 | 街道:序列 | 筹码:值 底池:值
                        boolean isLastAction = lastActionIndex.get(playerId) == (count - 1);
                        Integer expectedStack = playerStacks.get(playerId);
                        
                        System.out.printf("  %2d) [座%d] %-12s 【%-12s】%7s | %s:%d/%d | 筹码:%s",
                            count,
                            action.get("seat_number"),
                            playerId,
                            actionType,
                            amount,
                            action.get("street"),
                            action.get("action_sequence"),
                            action.get("global_sequence"),
                            stackAfter);
                        
                        // 验证筹码（只验证最后一条记录）
                        if (isLastAction && expectedStack != null && stackAfter != null) {
                            if (stackAfter.intValue() != expectedStack) {
                                System.out.printf("⚠️[预期:%d]", expectedStack);
                                hasError = true;
                            } else {
                                System.out.print("✓");
                            }
                        }
                        
                        // 底池
                        System.out.printf(" 底池:%s", potAfter);
                        if (count == actions.size() && potAfter != null && potAfter.intValue() == currentPot) {
                            System.out.print("✓");
                        }
                        System.out.println();
                    }
                    
                    System.out.println("  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
                    System.out.printf("  总计: %d 条", count);
                    if (hasError) {
                        System.out.print(" ⚠️  存在数据不一致");
                    } else {
                        System.out.print(" ✓ 数据一致");
                    }
                    System.out.println();
                    
                    // 更新最大序列号
                    lastGlobalSequence = maxSequence;
                }
            }
            
        } catch (Exception e) {
            System.err.println("❌ 查询失败: " + e.getMessage());
            e.printStackTrace();
        }
        
        System.out.println();
    }
    
    /**
     * 等待用户确认
     */
    private void waitForConfirmation(String message) {
        System.out.println();
        System.out.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
        System.out.println("  " + message);
        System.out.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
        System.out.print("按回车键继续...");
        System.out.flush();
        try {
            new java.util.Scanner(System.in).nextLine();
        } catch (Exception e) {
            // ignore
        }
        System.out.println();
    }
    
    /**
     * 运行预设场景
     */
    public void runScenario() {
        try {
            System.out.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
            System.out.println("  预设场景自动化测试");
            System.out.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
            System.out.println();
            System.out.println("场景: " + scenario.getScenarioName());
            System.out.println("桌子ID: " + scenario.getTableId());
            System.out.println("盲注: " + scenario.getSmallBlind() + "/" + scenario.getBigBlind());
            System.out.println();
            
            // 1. 连接服务器
            System.out.println("正在连接服务器...");
            tcpClient.connect().get(5, java.util.concurrent.TimeUnit.SECONDS);
            System.out.println("✅ 已连接到服务器");
            System.out.println();
            
            // 2. 创建手牌
            createHand();
            queryAndPrintDB("手牌创建完成");
            waitForConfirmation("手牌创建成功，准备执行翻前行动");
            
            // 3. 执行翻前行动
            executeStreetActions("preflop");
            queryAndPrintDB("翻前行动完成");
            waitForConfirmation("翻前行动完成");
            
            // 4. 检查是否需要继续
            if (hasMultiplePlayersInHand()) {
                // 翻牌圈
                transitionToStreet("flop");
                queryHandStateOnly("街道转换 → 翻牌圈");  // 验证街道状态
                waitForConfirmation("进入翻牌圈，准备执行行动");
                executeStreetActions("flop");
                queryAndPrintDB("翻牌圈行动完成");
                waitForConfirmation("翻牌圈行动完成");
            }
            
            if (hasMultiplePlayersInHand()) {
                // 转牌圈
                transitionToStreet("turn");
                queryHandStateOnly("街道转换 → 转牌圈");  // 验证街道状态
                waitForConfirmation("进入转牌圈，准备执行行动");
                executeStreetActions("turn");
                queryAndPrintDB("转牌圈行动完成");
                waitForConfirmation("转牌圈行动完成");
            }
            
            if (hasMultiplePlayersInHand()) {
                // 河牌圈
                transitionToStreet("river");
                queryHandStateOnly("街道转换 → 河牌圈");  // 验证街道状态
                waitForConfirmation("进入河牌圈，准备执行行动");
                executeStreetActions("river");
                queryAndPrintDB("河牌圈行动完成");
                waitForConfirmation("河牌圈行动完成");
            }
            
            // 5. 完成手牌
            completeHand();
            queryAndPrintDB("手牌完成");
            
            System.out.println();
            System.out.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
            System.out.println("  场景执行完成");
            System.out.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
            
        } catch (Exception e) {
            System.err.println("❌ 场景执行失败: " + e.getMessage());
            e.printStackTrace();
        } finally {
            try {
                tcpClient.disconnect();
            } catch (Exception e) {
                // ignore
            }
            
            // 关闭数据库连接
            if (dbConnection != null) {
                try {
                    dbConnection.close();
                } catch (Exception e) {
                    // ignore
                }
            }
        }
    }
    
    /**
     * 发送请求并等待响应
     */
    private Map<String, Object> sendRequest(short protocolId, Map<String, Object> request) throws Exception {
        // 打印请求
        String protocolName = getProtocolName(protocolId);
        System.out.println();
        System.out.println("━━━ 请求 [" + protocolName + "] ━━━");
        System.out.println(formatJson(request));
        
        // 发送请求（AI分析需要更长超时，因为可能需要10-15秒）
        int timeout = (protocolId == ProtocolConstants.AI_ANALYZE_REQ) ? 20 : 10;
        CompletableFuture<String> future = tcpClient.sendMessage(protocolId, request, timeout, java.util.concurrent.TimeUnit.SECONDS);
        String responseJson = future.get();
        @SuppressWarnings("unchecked")
        Map<String, Object> response = objectMapper.readValue(responseJson, Map.class);
        
        // 打印响应
        System.out.println();
        System.out.println("━━━ 响应 [" + protocolName + "] ━━━");
        System.out.println(formatJson(response));
        System.out.println();
        
        return response;
    }
    
    /**
     * 获取协议名称 (V4.10)
     */
    private String getProtocolName(short protocolId) {
        switch (protocolId) {
            case 2001: return "CREATE_HAND";
            case 2002: return "RECORD_ACTION";
            case 2003: return "TRANSITION_STREET";
            case 2004: return "COMPLETE_HAND";
            case 3001: return "AI_ANALYZE";
            case 9001: return "HEARTBEAT";
            default: return "PROTOCOL_" + protocolId;
        }
    }
    
    /**
     * 格式化JSON（紧凑单行）
     */
    private String formatJson(Object obj) {
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (Exception e) {
            return obj.toString();
        }
    }
    
    /**
     * 创建手牌
     */
    private void createHand() throws Exception {
        System.out.println("【步骤1】创建手牌");
        
        // 生成hand_id
        String generatedHandId = "PRESET_H_" + System.currentTimeMillis();
        
        Map<String, Object> request = new HashMap<>();
        request.put("hand_id", generatedHandId);
        request.put("table_id", scenario.getTableId());
        
        // V4.10: 盲注字段是必需的
        request.put("small_blind", scenario.getSmallBlind());
        request.put("big_blind", scenario.getBigBlind());
        request.put("ante", 0);
        request.put("game_type", "holdem");  // 默认德州扑克
        
        // 玩家列表（V4.31：创建手牌时不发送hole_cards，后续通过AI分析请求时再使用）
        List<Map<String, Object>> playersList = new ArrayList<>();
        int seatNumber = 1;
        Integer buttonSeat = null;
        for (PresetScenario.PlayerConfig player : scenario.getPlayers().values()) {
            Map<String, Object> playerMap = new HashMap<>();
            playerMap.put("player_id", player.getPlayerId());
            playerMap.put("position", player.getPosition());
            playerMap.put("seat_number", seatNumber);
            playerMap.put("stack", player.getInitialStack());
            // 不再发送hole_cards，因为CreateHandRequest不支持
            playersList.add(playerMap);
            
            // 记录BTN玩家的座位号
            if ("BTN".equals(player.getPosition())) {
                buttonSeat = seatNumber;
            }
            seatNumber++;
        }
        request.put("players", playersList);
        
        // 设置庄家按钮位置（如果没有BTN，默认为第1个座位）
        request.put("button_seat", buttonSeat != null ? buttonSeat : 1);
        
        // 不再发送board_cards，因为CreateHandRequest不支持
        // 公共牌将在街道转换时发送
        
        Map<String, Object> response = sendRequest(ProtocolConstants.CREATE_HAND_REQ, request);
        
        if ((int) response.get("code") == 200) {
            // 使用我们生成的hand_id
            this.handId = generatedHandId;
            System.out.println("✅ 手牌创建成功: " + handId);
            
            // V4.8: 初始底池为 0，盲注将通过主动发起累加
            currentPot = 0;
            
            // V4.8: 初始化玩家筹码（暂不扣除盲注）
            for (PresetScenario.PlayerConfig player : scenario.getPlayers().values()) {
                playerStacks.put(player.getPlayerId(), player.getInitialStack());
                playerCurrentBets.put(player.getPlayerId(), 0);
            }
            
            printGameState("翻前");
        } else {
            throw new RuntimeException("创建手牌失败: " + response.get("desc"));
        }
        System.out.println();
        
        // V4.8: 创建手牌后，主动发起盲注行动
        postBlinds();
    }
    
    /**
     * V4.8: 主动发起盲注行动
     * 顺序：小盲 → 大盲 → Straddle（如果有）
     */
    private void postBlinds() throws Exception {
        System.out.println("【步骤2】发起盲注");
        System.out.println();
        
        // 1. 发起小盲
        PresetScenario.PlayerConfig sbPlayer = findBlindPlayer("SB");
        if (sbPlayer != null) {
            postBlindAction(sbPlayer, "small_blind", scenario.getSmallBlind());
        }
        
        // 2. 发起大盲
        PresetScenario.PlayerConfig bbPlayer = findBlindPlayer("BB");
        if (bbPlayer != null) {
            postBlindAction(bbPlayer, "big_blind", scenario.getBigBlind());
        }
        
        // 3. TODO: 发起 Straddle（如果场景配置了）
        // if (scenario.hasStraddle()) {
        //     postBlindAction(straddlePlayer, "straddle", straddleAmount);
        // }
        
        System.out.println();
    }
    
    /**
     * V4.8: 查找盲注玩家
     * 2人桌：BTN兼任SB
     * 多人桌：SB单独存在
     */
    private PresetScenario.PlayerConfig findBlindPlayer(String blindPosition) {
        if ("SB".equals(blindPosition)) {
            // 先查找SB位置
            for (PresetScenario.PlayerConfig player : scenario.getPlayers().values()) {
                if ("SB".equals(player.getPosition())) {
                    return player;
                }
            }
            // 2人桌：BTN兼任SB
            if (scenario.getPlayers().size() == 2) {
                for (PresetScenario.PlayerConfig player : scenario.getPlayers().values()) {
                    if ("BTN".equals(player.getPosition())) {
                        return player;
                    }
                }
            }
        } else if ("BB".equals(blindPosition)) {
            for (PresetScenario.PlayerConfig player : scenario.getPlayers().values()) {
                if ("BB".equals(player.getPosition())) {
                    return player;
                }
            }
        }
        return null;
    }
    
    /**
     * V4.8: 发起单个盲注行动
     */
    private void postBlindAction(PresetScenario.PlayerConfig player, String actionType, int amount) throws Exception {
        int beforeStack = playerStacks.get(player.getPlayerId());
        int beforePot = currentPot;
        int afterStack = beforeStack - amount;
        int afterPot = beforePot + amount;
        
        Map<String, Object> request = new HashMap<>();
        request.put("table_id", scenario.getTableId());
        request.put("hand_id", handId);
        request.put("player_id", player.getPlayerId());
        request.put("street", "preflop");
        request.put("action_type", actionType);  // V4.8: small_blind/big_blind/straddle
        request.put("before_stack", beforeStack);
        request.put("before_pot", beforePot);
        request.put("amount", amount);
        request.put("after_stack", afterStack);
        request.put("after_pot", afterPot);
        
        System.out.println("━━━ 请求 [RECORD_ACTION - " + getActionTypeChinese(actionType) + "] ━━━");
        System.out.println(formatJson(request));
        System.out.println();
        
        Map<String, Object> response = sendRequest(ProtocolConstants.RECORD_ACTION_REQ, request);
        
        System.out.println("━━━ 响应 [RECORD_ACTION] ━━━");
        System.out.println(formatJson(response));
        System.out.println();
        
        if ((int) response.get("code") == 200) {
            // 更新本地状态
            playerStacks.put(player.getPlayerId(), afterStack);
            playerCurrentBets.put(player.getPlayerId(), amount);
            currentPot = afterPot;
            
            // 同步更新 GameStateTracker
            gameState.recordAction(player.getPlayerId(), actionType, amount);
            
            System.out.println("  ✓ " + player.getPosition() + " " + getActionTypeChinese(actionType) + " " + amount);
        } else {
            throw new RuntimeException("发起盲注失败: " + response.get("desc"));
        }
        System.out.println();
    }
    
    /**
     * V4.8: 获取行动类型的中文名称
     */
    private String getActionTypeChinese(String actionType) {
        switch (actionType) {
            case "small_blind": return "小盲";
            case "big_blind": return "大盲";
            case "straddle": return "抢抓";
            case "fold": return "弃牌";
            case "check": return "过牌";
            case "call": return "跟注";
            case "bet": return "下注";
            case "raise": return "加注";
            case "all-in": return "全下";
            default: return actionType;
        }
    }
    
    /**
     * 执行指定街道的行动
     */
    private void executeStreetActions(String street) throws Exception {
        System.out.println("【" + getStreetChinese(street) + "】执行行动");
        
        // 更新当前街道
        this.currentStreet = street;
        
        List<PresetScenario.ActionConfig> actions = scenario.getStreetActions().get(street);
        if (actions == null || actions.isEmpty()) {
            System.out.println("  无行动");
            return;
        }
        
        for (PresetScenario.ActionConfig action : actions) {
            // 检查玩家是否已经弃牌
            if (foldedPlayers.contains(action.getPlayerId())) {
                System.out.printf("  ⏭️  跳过已弃牌玩家: %s%n", getPlayerPosition(action.getPlayerId()));
                continue;
            }
            
            // 检查是否只剩一个玩家（其他人都弃牌了）
            if (countActivePlayers() <= 1) {
                System.out.println("  ⏭️  只剩一个玩家，跳过剩余行动");
                break;
            }
            
            // 使用 AI 决策版本
            executeActionWithAI(action);
            Thread.sleep(500); // 延迟，便于观察（AI调用需要更多时间）
        }
        
        System.out.println();
    }
    
    /**
     * 执行单个行动
     */
    private void executeAction(PresetScenario.ActionConfig action) throws Exception {
        String playerId = action.getPlayerId();
        String actionType = action.getAction();
        int amount = action.getAmount();
        
        // 获取玩家当前状态
        int beforeStack = playerStacks.get(playerId);
        int beforePot = currentPot;
        int currentBet = playerCurrentBets.get(playerId);
        
        // 计算after状态
        int afterStack = beforeStack;
        int afterPot = beforePot;
        
        // 修正 action_type：区分 bet 和 raise
        if ("raise".equalsIgnoreCase(actionType) && amount > 0) {
            // 检查是否有人已经下注
            boolean hasAnyoneBet = playerCurrentBets.values().stream().anyMatch(bet -> bet > 0);
            if (!hasAnyoneBet) {
                // 没有人下注，这是第一个下注 → 改为 bet
                actionType = "bet";
            }
            // 否则保持 raise
        }
        
        if ("fold".equalsIgnoreCase(actionType)) {
            // 弃牌，筹码和底池不变
        } else if ("check".equalsIgnoreCase(actionType)) {
            // 过牌，筹码和底池不变
        } else if ("call".equalsIgnoreCase(actionType)) {
            afterStack = beforeStack - amount;
            afterPot = beforePot + amount;
            currentBet += amount;
        } else if ("raise".equalsIgnoreCase(actionType) || "bet".equalsIgnoreCase(actionType)) {
            afterStack = beforeStack - amount;
            afterPot = beforePot + amount;
            currentBet += amount;
        }
        
        // 发送行动请求
        Map<String, Object> request = new HashMap<>();
        request.put("table_id", scenario.getTableId());
        request.put("hand_id", handId);
        request.put("player_id", playerId);
        request.put("action_type", actionType);
        request.put("amount", amount);
        request.put("before_stack", beforeStack);
        request.put("before_pot", beforePot);
        request.put("after_stack", afterStack);
        request.put("after_pot", afterPot);
        
        Map<String, Object> response = sendRequest(ProtocolConstants.RECORD_ACTION_REQ, request);
        
        if ((int) response.get("code") == 200) {
            // 更新本地状态
            playerStacks.put(playerId, afterStack);
            playerCurrentBets.put(playerId, currentBet);
            currentPot = afterPot;
            
            // 记录弃牌玩家
            if ("fold".equalsIgnoreCase(actionType)) {
                foldedPlayers.add(playerId);
            }
            
            String position = getPlayerPosition(playerId);
            System.out.println("  ✓ " + position + " " + getActionChinese(actionType) + " " + amount);
        } else {
            throw new RuntimeException("行动记录失败: " + response.get("desc"));
        }
    }
    
    /**
     * 街道转换
     */
    private void transitionToStreet(String newStreet) throws Exception {
        System.out.println("【街道转换】→ " + getStreetChinese(newStreet));
        
        // 重置当前下注
        for (String playerId : playerCurrentBets.keySet()) {
            playerCurrentBets.put(playerId, 0);
        }
        
        // 获取公共牌
        List<String> boardCards = getBoardCardsForStreet(newStreet);
        
        // 更新当前街道
        this.currentStreet = newStreet;
        
        // 同步更新 GameStateTracker
        gameState.transitionStreet(newStreet, boardCards);
        
        // 计算还在局的玩家数量
        int activePlayers = countActivePlayers();
        
        Map<String, Object> request = new HashMap<>();
        request.put("table_id", scenario.getTableId());
        request.put("hand_id", handId);
        request.put("street", newStreet);
        request.put("board_cards", boardCards);
        request.put("active_players", activePlayers);
        request.put("pot_after", currentPot);
        
        Map<String, Object> response = sendRequest(ProtocolConstants.TRANSITION_STREET_REQ, request);
        
        if ((int) response.get("code") == 200) {
            System.out.println("  公共牌: " + String.join(" ", boardCards));
            System.out.println("  底池: " + currentPot);
        } else {
            throw new RuntimeException("街道转换失败: " + response.get("desc"));
        }
        System.out.println();
    }
    
    /**
     * 完成手牌
     */
    private void completeHand() throws Exception {
        System.out.println("【步骤N】完成手牌");
        
        // 获取活跃玩家（未弃牌）
        List<PresetScenario.PlayerConfig> activePlayers = scenario.getPlayers().values().stream()
            .filter(p -> !isFolded(p.getPlayerId()))
            .collect(java.util.stream.Collectors.toList());
        
        int rakeAmount = 0;  // 无抽水
        int potToDistribute = currentPot - rakeAmount;
        
        // 确定赢家和底池分配
        String winnerId = null;
        Map<String, Integer> finalStacksMap = new HashMap<>();
        Map<String, Integer> netResultsMap = new HashMap<>();
        Map<String, Integer> totalInvestmentMap = new HashMap<>();
        
        if (activePlayers.size() == 1) {
            // 只有一人，该玩家赢得底池
            winnerId = activePlayers.get(0).getPlayerId();
        } else {
            // 多人摊牌：使用预设场景的赢家配置
            winnerId = scenario.getWinnerId();
            if (winnerId == null || winnerId.isEmpty()) {
                // 如果没有配置赢家，默认第一个活跃玩家获胜
                winnerId = activePlayers.get(0).getPlayerId();
                System.out.println("  ⚠️  未配置赢家，默认 " + winnerId + " 获胜");
            }
        }
        
        // 显示摊牌信息
        System.out.println();
        System.out.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
        System.out.println("  【摊牌 Showdown】");
        System.out.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
        for (PresetScenario.PlayerConfig player : activePlayers) {
            String playerId = player.getPlayerId();
            String holeCards = String.join(" ", player.getHoleCards());
            System.out.printf("  %s: %s%n", playerId, holeCards);
        }
        System.out.println();
        System.out.printf("  🏆 赢家: %s%n", winnerId);
        System.out.printf("  💰 赢得底池: %d%n", potToDistribute);
        System.out.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
        System.out.println();
        
        // 第一步：计算每个玩家的总投入（包括盲注）
        for (PresetScenario.PlayerConfig player : scenario.getPlayers().values()) {
            String playerId = player.getPlayerId();
            int initialStack = player.getStack();
            int currentStack = playerStacks.get(playerId);
            int totalInvestment = initialStack - currentStack;
            totalInvestmentMap.put(playerId, totalInvestment);
        }
        
        // 第二步：计算最终筹码
        // V4.7: Run It Twice 特殊处理
        boolean isRIT = scenario.getRunItTwice() != null && scenario.getRunItTwice().isEnabled();
        Map<String, Integer> ritWinnings = new HashMap<>();
        
        if (isRIT) {
            // RIT模式：根据每次运行的结果累计每个玩家的获得
            for (PresetScenario.RunResult run : scenario.getRunItTwice().getRuns()) {
                for (PresetScenario.PlayerRunDetail detail : run.getDetails()) {
                    String playerId = detail.getPlayerId();
                    int currentWin = ritWinnings.getOrDefault(playerId, 0);
                    ritWinnings.put(playerId, currentWin + detail.getResult());
                }
            }
        }
        
        // 计算所有玩家的最终筹码
        for (PresetScenario.PlayerConfig player : scenario.getPlayers().values()) {
            String playerId = player.getPlayerId();
            int initialStack = player.getStack();
            int totalInvestment = totalInvestmentMap.get(playerId);
            
            int finalStack;
            if (isRIT && ritWinnings.containsKey(playerId)) {
                // RIT玩家：初始 - 投入 + RIT获得
                int ritWin = ritWinnings.get(playerId);
                finalStack = initialStack - totalInvestment + ritWin;
            } else if (playerId.equals(winnerId)) {
                // 普通赢家：初始 - 投入 + 赢得底池
                finalStack = initialStack - totalInvestment + potToDistribute;
            } else {
                // 输家/弃牌：初始 - 投入
                finalStack = initialStack - totalInvestment;
            }
            
            finalStacksMap.put(playerId, finalStack);
            netResultsMap.put(playerId, finalStack - initialStack);
        }
        
        // 显示最终筹码和盈亏
        System.out.println("【最终筹码 & 盈亏】");
        for (PresetScenario.PlayerConfig player : scenario.getPlayers().values()) {
            String playerId = player.getPlayerId();
            int initialStack = player.getStack();
            int finalStack = finalStacksMap.get(playerId);
            int netResult = netResultsMap.get(playerId);
            
            String status;
            String trophy = "";
            if (isFolded(playerId)) {
                status = "[弃牌]";
            } else if (isRIT && ritWinnings.containsKey(playerId)) {
                // RIT玩家：显示是赢还是输
                if (netResult > 0) {
                    status = "[赢]";
                    trophy = "🏆";
                } else if (netResult < 0) {
                    status = "[输]";
                } else {
                    status = "[平]";
                }
            } else if (playerId.equals(winnerId)) {
                status = "[赢]";
                trophy = "🏆";
            } else {
                status = "[输]";
            }
            
            System.out.printf("  %s %-12s: %5d → %5d (%+d) %s%n",
                status, playerId, initialStack, finalStack, netResult, trophy);
        }
        System.out.println();
        
        // 构建符合HandCompletionRequest的请求
        Map<String, Object> request = new HashMap<>();
        request.put("table_id", scenario.getTableId());
        request.put("hand_id", handId);
        request.put("completion_type", activePlayers.size() == 1 ? "fold_win" : "showdown");
        request.put("players_to_showdown", activePlayers.size());
        request.put("final_pot", currentPot);
        request.put("rake_amount", rakeAmount);
        
        // 构建final_results（✅ V4.8修复：添加player_id和cards字段）
        List<Map<String, Object>> finalResults = new ArrayList<>();
        for (PresetScenario.PlayerConfig player : scenario.getPlayers().values()) {
            Map<String, Object> result = new HashMap<>();
            String playerId = player.getPlayerId();
            
            // ✅ 新增字段1：player_id
            result.put("player_id", playerId);
            
            // 现有字段
            result.put("seat_number", getSeatNumber(playerId));
            result.put("player_name", playerId);
            result.put("final_stack", finalStacksMap.get(playerId));
            result.put("net_result", netResultsMap.get(playerId));
            
            // ✅ 新增字段2：cards（实际底牌）
            result.put("cards", player.getHoleCards());
            
            // ✅ 新增字段3：cards_shown（展示的牌，弃牌为空）
            if (isFolded(playerId)) {
                result.put("cards_shown", new ArrayList<>());  // 弃牌：不展示
            } else {
                result.put("cards_shown", player.getHoleCards());  // 到摊牌：展示
            }
            
            // 可选字段：hand_rank 和 hand_description（暂不添加，留给后续扩展）
            
            finalResults.add(result);
        }
        request.put("final_results", finalResults);
        
        // V4.6: 添加 Run It Twice 支持
        if (scenario.getRunItTwice() != null && scenario.getRunItTwice().isEnabled()) {
            Map<String, Object> runItTwiceData = new HashMap<>();
            runItTwiceData.put("enabled", true);
            
            List<Map<String, Object>> runsData = new ArrayList<>();
            for (PresetScenario.RunResult run : scenario.getRunItTwice().getRuns()) {
                Map<String, Object> runData = new HashMap<>();
                runData.put("run", run.getRun());
                runData.put("board", run.getBoard());
                runData.put("pot_share", run.getPotShare());
                runData.put("winners", run.getWinners());
                
                List<Map<String, Object>> detailsData = new ArrayList<>();
                for (PresetScenario.PlayerRunDetail detail : run.getDetails()) {
                    Map<String, Object> detailData = new HashMap<>();
                    detailData.put("player_id", detail.getPlayerId());
                    detailData.put("hand_rank", detail.getHandRank());
                    detailData.put("hand_desc", detail.getHandDesc());
                    detailData.put("result", detail.getResult());
                    detailsData.add(detailData);
                }
                runData.put("details", detailsData);
                runsData.add(runData);
            }
            runItTwiceData.put("runs", runsData);
            request.put("run_it_twice", runItTwiceData);
            
            System.out.println("✨ Run It Twice 已启用 - " + runsData.size() + " 次运行");
        }
        
        Map<String, Object> response = sendRequest(ProtocolConstants.COMPLETE_HAND_REQ, request);
        
        if ((int) response.get("code") == 200) {
            System.out.println("✅ 手牌完成并提交到服务器");
        } else {
            System.err.println("❌ 完成手牌失败: " + response.get("desc"));
        }
    }
    
    /**
     * 获取玩家座位号（根据玩家在players map中的顺序）
     */
    private int getSeatNumber(String playerId) {
        int seatNumber = 1;
        for (PresetScenario.PlayerConfig player : scenario.getPlayers().values()) {
            if (player.getPlayerId().equals(playerId)) {
                return seatNumber;
            }
            seatNumber++;
        }
        return 0;
    }
    
    /**
     * 打印游戏状态
     */
    private void printGameState(String street) {
        System.out.println();
        System.out.println("━━━ 游戏状态 ━━━");
        System.out.println("街道: " + street);
        System.out.println("底池: " + currentPot);
        System.out.println("玩家:");
        for (PresetScenario.PlayerConfig player : scenario.getPlayers().values()) {
            int stack = playerStacks.get(player.getPlayerId());
            int bet = playerCurrentBets.get(player.getPlayerId());
            String cards = String.join(" ", player.getHoleCards());
            System.out.println("  " + player.getPosition() + " (" + player.getPlayerId() + "): " + 
                             stack + " 筹码, " + bet + " 下注, " + cards);
        }
        System.out.println();
    }
    
    /**
     * 计算还在局的玩家数量
     */
    private int countActivePlayers() {
        int activeCount = 0;
        for (PresetScenario.PlayerConfig player : scenario.getPlayers().values()) {
            if (!foldedPlayers.contains(player.getPlayerId())) {
                activeCount++;
            }
        }
        return activeCount;
    }
    
    /**
     * 检查是否有多个玩家还在局
     */
    private boolean hasMultiplePlayersInHand() {
        return countActivePlayers() > 1;
    }
    
    /**
     * 检查玩家是否已弃牌
     */
    private boolean isFolded(String playerId) {
        return foldedPlayers.contains(playerId);
    }
    
    private String getPlayerPosition(String playerId) {
        for (PresetScenario.PlayerConfig player : scenario.getPlayers().values()) {
            if (player.getPlayerId().equals(playerId)) {
                return player.getPosition();
            }
        }
        return playerId;
    }
    
    private String getStreetChinese(String street) {
        switch (street.toLowerCase()) {
            case "preflop": return "翻前";
            case "flop": return "翻牌圈";
            case "turn": return "转牌圈";
            case "river": return "河牌圈";
            default: return street;
        }
    }
    
    private String getActionChinese(String action) {
        switch (action.toLowerCase()) {
            case "fold": return "弃";
            case "check": return "过";
            case "call": return "跟";
            case "raise": return "加";
            default: return action;
        }
    }
    
    // ============================================
    // AI 决策集成方法
    // ============================================
    
    /**
     * 构建 AI 分析请求
     * 
     * 注意：SimpleTcpClient.sendRequest() 会自动包装 data 字段，
     * 所以这里直接返回业务数据，不要再包装一层 data
     */
    private Map<String, Object> buildAIAnalysisRequest(String playerId) {
        PresetScenario.PlayerConfig player = scenario.getPlayers().get(playerId);
        if (player == null) {
            throw new IllegalArgumentException("Unknown player: " + playerId);
        }
        
        // 转换底牌为数字编码
        List<Integer> heroCards = CardEncoder.encodeList(player.getHoleCards());
        
        // 计算 active_opponents (2人场景固定为1)
        int activeOpponents = scenario.getPlayers().size() - 1 - foldedPlayers.size();
        
        // 获取当前街道的公共牌
        List<String> boardCards = getBoardCardsForStreet(currentStreet);
        
        Map<String, Object> request = new HashMap<>();
        request.put("table_id", scenario.getTableId());
        request.put("hand_id", handId);
        request.put("player_id", playerId);
        request.put("hero_position", player.getPosition());
        request.put("hero_cards", heroCards);
        request.put("hero_stack", gameState.getCurrentStack(playerId));
        request.put("street", currentStreet);
        request.put("board_cards", boardCards);
        request.put("pot", gameState.getPot());
        request.put("to_call", gameState.calculateToCall(playerId));
        request.put("active_opponents", activeOpponents);
        
        return request;
    }
    
    /**
     * 根据街道获取公共牌
     */
    private List<String> getBoardCardsForStreet(String street) {
        List<String> allBoardCards = scenario.getBoardCards();
        if (allBoardCards == null || allBoardCards.isEmpty()) {
            return new ArrayList<>();
        }
        
        switch (street.toLowerCase()) {
            case "preflop":
                return new ArrayList<>();
            case "flop":
                return allBoardCards.size() >= 3 ? allBoardCards.subList(0, 3) : new ArrayList<>();
            case "turn":
                return allBoardCards.size() >= 4 ? allBoardCards.subList(0, 4) : new ArrayList<>();
            case "river":
                return allBoardCards.size() >= 5 ? allBoardCards.subList(0, 5) : allBoardCards;
            default:
                return new ArrayList<>();
        }
    }
    
    /**
     * 请求 AI 分析
     */
    private Map<String, Object> requestAIAnalysis(String playerId) throws Exception {
        System.out.println();
        System.out.println("🤖 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
        System.out.println("  【AI 分析】" + getPlayerPosition(playerId));
        System.out.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
        
        Map<String, Object> request = buildAIAnalysisRequest(playerId);
        
        // 打印关键信息
        PresetScenario.PlayerConfig player = scenario.getPlayers().get(playerId);
        System.out.printf("  底牌: %s (编码: %s)%n", player.getHoleCards(), request.get("hero_cards"));
        System.out.printf("  筹码: %d | 底池: %d | 需跟注: %d%n",
            request.get("hero_stack"), request.get("pot"), request.get("to_call"));
        System.out.printf("  街道: %s | 公共牌: %s%n", currentStreet, request.get("board_cards"));
        
        // 发送 AI 分析请求
        Map<String, Object> response = sendRequest(ProtocolConstants.AI_ANALYZE_REQ, request);
        
        return response;
    }
    
    /**
     * 解析 AI 响应并决策
     * 
     * 策略：采用 AI 建议为主，GTO 用于对比
     */
    private AIDecision parseAIResponse(Map<String, Object> response) {
        AIDecision decision = new AIDecision();
        
        if ((int) response.get("code") != 200) {
            System.err.println("❌ AI 分析失败: " + response.get("desc"));
            decision.useFallback = true;
            return decision;
        }
        
        Map<String, Object> data = (Map<String, Object>) response.get("data");
        
        // 获取 GTO 建议（用于对比）
        Map<String, Double> gto = (Map<String, Double>) data.get("gto");
        
        // 获取 AI 建议（主要采用）
        Map<String, Object> ai = (Map<String, Object>) data.get("ai");
        
        // 获取胜率信息
        Double equity = (Double) data.get("equity");
        Double handStrength = (Double) data.get("hand_strength");
        
        // 打印 GTO 建议（对比用）
        System.out.println();
        System.out.println("  【GTO 混合策略】（对比参考）");
        if (gto != null) {
            gto.entrySet().stream()
                .sorted(Map.Entry.<String, Double>comparingByValue().reversed())
                .forEach(e -> System.out.printf("    %s: %.1f%%%n", e.getKey(), e.getValue() * 100));
        }
        
        // 打印 AI 建议（主要采用）
        System.out.println();
        System.out.println("  【AI 建议】（实际执行）✅");
        if (ai != null) {
            String action = (String) ai.get("action");
            Object amountObj = ai.get("amount");
            int amount = 0;
            if (amountObj instanceof Integer) {
                amount = (Integer) amountObj;
            } else if (amountObj instanceof Double) {
                amount = ((Double) amountObj).intValue();
            }
            Double confidence = (Double) ai.get("confidence");
            String reasoning = (String) ai.get("reasoning");
            
            System.out.printf("    行动: %s | 金额: %d%n", action, amount);
            System.out.printf("    信心度: %.1f%%%n", confidence * 100);
            System.out.printf("    理由: %s%n", reasoning != null ? reasoning : "无");
            
            decision.action = action;
            decision.amount = amount;
            decision.confidence = confidence;
        }
        
        // 打印牌力信息
        System.out.println();
        System.out.println("  【牌力评估】");
        System.out.printf("    胜率: %.1f%% | 手牌强度: %.2f%n", 
            equity != null ? equity * 100 : 0, 
            handStrength != null ? handStrength : 0);
        
        System.out.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
        
        return decision;
    }
    
    /**
     * AI 决策结果
     */
    private static class AIDecision {
        String action;
        int amount;
        Double confidence;
        boolean useFallback = false;  // 是否使用预设行动（AI失败时）
    }
    
    /**
     * 执行带 AI 决策的行动
     */
    private void executeActionWithAI(PresetScenario.ActionConfig presetAction) throws Exception {
        String playerId = presetAction.getPlayerId();
        
        // 判断是否是强制行动（小盲、大盲）
        boolean isForcedAction = presetAction.getAction().contains("blind");
        
        String finalAction;
        int finalAmount;
        
        if (!enableAI || isForcedAction) {
            // 不启用AI 或 强制行动 → 使用预设
            finalAction = presetAction.getAction();
            finalAmount = presetAction.getAmount();
            
            if (isForcedAction) {
                System.out.println("  [强制行动] " + getPlayerPosition(playerId) + " - " + finalAction);
            }
        } else {
            // 非强制行动 → 调用AI分析
            Map<String, Object> aiResponse = requestAIAnalysis(playerId);
            AIDecision decision = parseAIResponse(aiResponse);
            
            if (decision.useFallback) {
                // AI 失败，使用预设
                System.out.println("  ⚠️ AI 分析失败，使用预设行动");
                finalAction = presetAction.getAction();
                finalAmount = presetAction.getAmount();
            } else {
                // 使用 AI 建议
                finalAction = decision.action;
                finalAmount = decision.amount;
                
                // 对比预设和AI建议
                if (!finalAction.equals(presetAction.getAction()) || finalAmount != presetAction.getAmount()) {
                    System.out.printf("%n  📊 对比: 预设[%s %d] → AI[%s %d]%n",
                        presetAction.getAction(), presetAction.getAmount(),
                        finalAction, finalAmount);
                }
            }
        }
        
        // 执行最终行动（复用原有逻辑）
        executeActionInternal(playerId, finalAction, finalAmount);
    }
    
    /**
     * 执行行动的内部逻辑（从原 executeAction 提取）
     */
    private void executeActionInternal(String playerId, String actionType, int amount) throws Exception {
        // 获取玩家当前状态
        int beforeStack = playerStacks.get(playerId);
        int beforePot = currentPot;
        int currentBet = playerCurrentBets.get(playerId);
        
        // 计算after状态
        int afterStack = beforeStack;
        int afterPot = beforePot;
        
        // 修正 action_type：区分 bet 和 raise
        if ("raise".equalsIgnoreCase(actionType) && amount > 0) {
            // 检查是否有人已经下注
            boolean hasAnyoneBet = playerCurrentBets.values().stream().anyMatch(bet -> bet > 0);
            if (!hasAnyoneBet) {
                // 没有人下注，这是第一个下注 → 改为 bet
                actionType = "bet";
            }
            // 否则保持 raise
        }
        
        // 强制 check 和 fold 的金额为 0
        if ("check".equalsIgnoreCase(actionType) || "fold".equalsIgnoreCase(actionType)) {
            amount = 0;
        }
        
        if ("fold".equalsIgnoreCase(actionType)) {
            // 弃牌，筹码和底池不变
        } else if ("check".equalsIgnoreCase(actionType)) {
            // 过牌，筹码和底池不变
        } else if ("call".equalsIgnoreCase(actionType)) {
            afterStack = beforeStack - amount;
            afterPot = beforePot + amount;
            currentBet += amount;
        } else if ("raise".equalsIgnoreCase(actionType) || "bet".equalsIgnoreCase(actionType)) {
            afterStack = beforeStack - amount;
            afterPot = beforePot + amount;
            currentBet += amount;
        }
        
        // 发送行动请求
        Map<String, Object> request = new HashMap<>();
        request.put("table_id", scenario.getTableId());
        request.put("hand_id", handId);
        request.put("player_id", playerId);
        request.put("action_type", actionType);
        request.put("amount", amount);
        request.put("before_stack", beforeStack);
        request.put("before_pot", beforePot);
        request.put("after_stack", afterStack);
        request.put("after_pot", afterPot);
        
        Map<String, Object> response = sendRequest(ProtocolConstants.RECORD_ACTION_REQ, request);
        
        if ((int) response.get("code") == 200) {
            // 更新本地状态
            playerStacks.put(playerId, afterStack);
            playerCurrentBets.put(playerId, currentBet);
            currentPot = afterPot;
            
            // 同时更新 GameStateTracker
            gameState.recordAction(playerId, actionType, amount);
            
            // 记录弃牌玩家
            if ("fold".equalsIgnoreCase(actionType)) {
                foldedPlayers.add(playerId);
            }
            
            String position = getPlayerPosition(playerId);
            System.out.println("  ✓ " + position + " " + getActionChinese(actionType) + " " + amount + " [AI驱动]");
        } else {
            throw new RuntimeException("行动记录失败: " + response.get("desc"));
        }
    }
    
    /**
     * 主方法 - 运行预设场景
     */
    public static void main(String[] args) {
        try {
            // 处理 list 命令 - 列出所有可用场景
            if (args.length > 0 && "list".equals(args[0])) {
                ScenarioRegistry.listAllScenarios();
                return;
            }
            
            // 选择场景：使用 ScenarioRegistry 来管理所有场景
            PresetScenario scenario;
            
            if (args.length > 0) {
                // 使用命令行参数指定场景名称
                String scenarioName = args[0];
                scenario = ScenarioRegistry.getScenario(scenarioName);
            } else {
                // 默认使用 players_2 场景
                scenario = ScenarioRegistry.getScenario("players_2");
            }
            
            // 运行场景
            PresetScenarioClient client = new PresetScenarioClient("127.0.0.1", 8889, scenario);
            
            // 默认启用数据库验证（使用开发环境MySQL配置）
            String jdbcUrl = System.getenv("DB_URL");
            String dbUser = System.getenv("DB_USER");
            String dbPassword = System.getenv("DB_PASSWORD");
            
            // 使用默认值（开发环境MySQL）
            if (jdbcUrl == null) jdbcUrl = "jdbc:mysql://127.0.0.1:3306/pokermind?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true";
            if (dbUser == null) dbUser = "root";
            if (dbPassword == null) dbPassword = "";  // 开发环境默认空密码
            
            client.connectDatabase(jdbcUrl, dbUser, dbPassword);
            
            client.runScenario();
            
        } catch (Exception e) {
            System.err.println("❌ 预设场景测试失败: " + e.getMessage());
            e.printStackTrace();
            System.exit(1);
        }
    }
}

